
//#define USE_BINARY_SERIALIZER
#define UseSoapFormatter
//#define Use_VersionTolerant_Serializer

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Xml;
using System.IO;
using System.Web;
using System.Drawing;
using System.Runtime.Serialization.Formatters.Soap;
using TrainGame.Common;
using TrainGame.AI;	// JoeB: needed to add this assembly to References in Solution explorer as well

namespace TrainGame.Model
{
/*
     /// <summary>
     /// Encapsulates all HTTP-specific information about an individual HTTP request.
     /// </summary>
     public class HttpContextHelper
     {
         /// <summary>
         /// Gets the System.Web.HttpApplicationState object for the current HTTP request.
         /// </summary>
         public static HttpApplicationState CurrentApplication
         {
             get { return HttpContext.Current.Application; }
         }

         /// <summary>
         /// Gets the System.Web.Caching.Cache object for the current HTTP request.
         /// </summary>
         public static Cache CurrentCache
         {
             get { return HttpContext.Current.Cache; }
         }

         /// <summary>
         /// Gets the System.Web.HttpRequest object for the current HTTP request.
         /// </summary>
         public static HttpRequest CurrentRequest
         {
             get { return HttpContext.Current.Request; }
         }

         /// <summary>
         /// Gets the System.Web.HttpResponse object for the current HTTP response.
         /// </summary>
         public static HttpResponse CurrentResponse
         {
             get { return HttpContext.Current.Response; }
         }

         /// <summary>
         /// Gets the System.Web.HttpServerUtility object that provides methods used in
         /// processing Web requests.
         /// </summary>
         public static HttpServerUtility CurrentServer
         {
             get { return HttpContext.Current.Server; }
         }

         /// <summary>
         /// Gets the System.Web.SessionState.HttpSessionState object for the current
         /// HTTP request.
         /// </summary>
         public static HttpSessionState CurrentSession
         {
             get { return HttpContext.Current.Session; }
         }
     }
 */

    public enum eOperatingRoundPhase
    {
        LayTile, PlaceToken, RunTrains, BuyTrains, RedeemLoans, Done
    }

    public enum eGamePhase
    {
        Phase2, Phase3, Phase4, Phase5, Phase6, Phase8, PhaseD
    }

	[Serializable]
    public class Game
    {
//        private StringCollection scPossibleCgrReplacementTokens = null;
        public bool CgrOptionalTokenReplacement { get; set; }        
        public int NumberPublicCompanies { get; set; }
        public const int NumberOfCompanies = 12;    // MaxPublicCompanies = 12;
        private string _MachineName = "";
        private string _baseFilePath = "";

        //		private ImageClickEventHandler			_globalHexHandler;

        /// <summary>
        /// The base name of the game, as used with files.
        /// </summary>
        private string sGameName;

        /// <summary>
        /// The name of the game as assigned when the game was initially started. Saved games will always 
        /// begin with this name.
        /// </summary>
        public string GameName
        {
            get { return sGameName; }
            set { sGameName = value; }
        }
        private string sCreator;

        public string Creator
        {
            get { return sCreator; }
            set { sCreator = value; }
        }

        private bool bInitialized = false;

        public bool Initialized
        {
            get { return bInitialized; }
            set { bInitialized = value; }
        }
        
        private bool started;
        public int nGameSavedNumber;

        private Tiles _aTiles;
        //private GameboardHexCollection _aGameboardHexes;
        private BidCollection _aBids;
        private PlayerCollection _aPlayers;
        private PrivateCompanies _aPrivateCompanies;
        private PublicCompanies _aPublicCompanies;
        private StockMarketPositionCollection _aStockMarketPositions;
        private TrainCollection _a2Trains;
        private TrainCollection _a3Trains;
        private TrainCollection _a4Trains;
        private TrainCollection _a5Trains;
        private TrainCollection _a6Trains;
        private TrainCollection _a8Trains;
        private TrainCollection _aDTrains;
        private TrainCollection _aOpenMarketTrains;
        private Hashtable /*MyDictionary<string, GamePhaseInfo>*/ _htGamePhaseInfo;

        private int _tunnelsForSale;	// 2 available for sale
        private int _bridgesForSale;	// 2 available for sale

        private bool _auctionPhase;
        private bool _privateSold;			// Used to determine if after all players have passed whether to go to operating round or reduce price of first private by $5
        private bool _firstStockRound;
        private int _passes;				// The number of passes in the current SR.
        private int _auctionPhasePasses;	// The number of passes in the current private company auction.
        private BidCollection _aCurrentInAuctionBids;
        private string _auctionPriorityPlayerName;
        private StringCollection _history;
        private StringCollection _rejoinedPlayers;  // JWF 10.27.2007  Players who are rejoining a restored game
        private string _name;
        private int _bank;
        private int _startingCapital;
        private bool _gameOver;
        private bool _bankBusted;
        private bool _bankruptcyOccurred;
        private bool _cgrFormed;
        private bool _cgrForming;
        private int _certificateLimit;
        private string _currentlyOperatingOwner;
        private bool _viewGameStatusFromStockRound;
        private bool _viewStockMarketFromStockRound;

        // operating round info
        private bool _canBuyPrivateCompanies;
        private bool _yellowTilesAvailable;
        private bool _greenTilesAvailable;
        private bool _brownTilesAvailable;
        private bool _grayTilesAvailable;
        private int _trainLimit;
        private bool _maxTrain8Train;
        private bool _allowUseOfOptional6Train;
        private bool _collusionAllowed;
        private int _costOfNextNewTrainAvailable;
        private int _sharesNeededtoOpen;
        private int _currentOperatingRound;
        private int _numberOfOperatingRoundsNow;
        private int _numberOfOperatingRoundsNext;
        private int _numberOfOperatingRoundsLeft;
        private bool _companyMustPlaceInitialToken;

        // exercising privates info
        private bool _exercisingTheCanadaCompany;
        private bool _exercisingWaterlooTileLay;
        private bool _exercisingWaterlooTokenPlacement;
        private bool _exercisingPort;

        // variables to handle the private company sales
        private Player _privateCompanyBuyer;
        private Player _privateCompanySeller;
        private PublicCompany _privateCompanyBuyingCompany;
        private PrivateCompany _privateCompanyForSale;
        private int _privateCompanySalePrice;
        private bool _privateCompanySale;
        private bool _privateCompanySold;
        private bool _privateCompanySalePending;
        private string _privateCompanyPriorityPlayerName;

        // variables to handle the existing train sales
        private Player _existingTrainBuyer;
        private Player _existingTrainSeller;
        private PublicCompany _existingTrainBuyingCompany;
        private PublicCompany _existingTrainSellingCompany;
        private int _existingTrainPurchasePrice;
        private bool _existingTrainPurchaseSale;
        private bool _existingTrainPurchased;
        private bool _existingTrainSalePending;
        private string _existingTrainToBuy;
        private string _existingTrainPriorityPlayerName;

        // variables to handle CGR formation
        private bool    _discard4s;
        private string  _cgrFormationPlayerName;
        private string  _cgrFormationPriorityPlayerName;

        private PublicCompany _currentlyOperatingCompany;
        private bool _operatingRoundBeingInitialized;
        private bool _GoToStockRound = true;
        private bool _tileAlreadyPlayed;
        private string _currentHex;		// The hex in which a tile was recently played.  Only this tile can be rotated.
        private string m_sPathOfLastSavedGame = "";  // JWF 10.13.07

        public bool Discard4s
        {
            get { return _discard4s; }
            set { _discard4s = value; }
        }

        public string PathOfLastSavedGame	// Force company to lay their initial token.
        {
            get { return m_sPathOfLastSavedGame; }
            set { m_sPathOfLastSavedGame = value; }
        }

        public bool CompanyMustPlaceInitialToken	// Force company to lay their initial token.
        {
            get { return _companyMustPlaceInitialToken; }
            set { _companyMustPlaceInitialToken = value; }
        }

        public string CurrentHex
        {
            get { return _currentHex; }
            set { _currentHex = value; }
        }

        //		public ImageClickEventHandler GlobalHexHandler
        //		{
        //			get { return _globalHexHandler; }
        //			set { _globalHexHandler = value; }
        //		}

        public bool TileAlreadyPlayed
        {
            get { return _tileAlreadyPlayed; }
            set { _tileAlreadyPlayed = value; }
        }

        private eGamePhase _gamePhase;
        private eOperatingRoundPhase _operatingRoundPhase;

        public eGamePhase GamePhase
        {
            get { return _gamePhase; }
            set { _gamePhase = value; }
        }

        public eOperatingRoundPhase OperatingRoundPhase
        {
            get { return _operatingRoundPhase; }
            set { _operatingRoundPhase = value; }
        }

        public bool CanBuyPrivateCompanies
        {
            get { return _canBuyPrivateCompanies; }
            set { _canBuyPrivateCompanies = value; }
        }

        public bool YellowTilesAvailable
        {
            get { return _yellowTilesAvailable; }
            set { _yellowTilesAvailable = value; }
        }

        public bool GreenTilesAvailable
        {
            get { return _greenTilesAvailable; }
            set { _greenTilesAvailable = value; }
        }

        public bool BrownTilesAvailable
        {
            get { return _brownTilesAvailable; }
            set { _brownTilesAvailable = value; }
        }

        public bool GrayTilesAvailable
        {
            get { return _grayTilesAvailable; }
            set { _grayTilesAvailable = value; }
        }

        public bool CanTownsBeUpgradedAndDownGraded
        {
            get
            {
				return GrayTilesAvailable;
            }
        }


        public bool GoToStockRound
        {
            get { return _GoToStockRound; }
            set { _GoToStockRound = value; }
        }

        public string CurrentOperatingOwner()
        {
            if (this._currentlyOperatingOwner.Length > 0)	// Cache this info for optimization purposes.
            {
                return this._currentlyOperatingOwner;
            }

            string sOwner = "";
            bool bPresidentFound = false;

            if (this.CurrentlyOperatingCompany != null)
            {
                foreach (Player player in this.Players)
                {
                    foreach (Share share in player.Shares)
                    {
                        if (share.President)
                        {
                            if (share.PublicCompany.Name == this.CurrentlyOperatingCompany.Name)
                            {
                                bPresidentFound = true;
                                sOwner = player.Name;
                                break;
                            }
                        }
                    }

                    if (bPresidentFound)
                    {
                        break;
                    }
                }
            }

            return sOwner;
        }

        public PublicCompany CurrentlyOperatingCompany
        {
            get { return _currentlyOperatingCompany; }
            set { _currentlyOperatingCompany = value; }
        }

        public bool IncrementOperatingRoundPhase()	// return true if this company is done operating
        {
            bool bReturn = false;

            this.OperatingRoundPhase++;

            if (this.OperatingRoundPhase == eOperatingRoundPhase.Done)
                bReturn = true;

            return bReturn;
        }

        public bool             ForcedStockSalePending      { get; set; }
        public bool             ForcedStockSaleCompleted    { get; set; }
        public bool             ForcedStockSaleFullfilled   { get; set; }
        public int              ForcedStockSaleAmountNeeded { get; set; }
        public int              ForcedStockSaleAmountSold   { get; set; }
        public Player           ForcedStockSalePlayer       { get; set; }
		//public bool				ForcedStockSalePlayerPriority { get; set; }
		public Player			ForcedStockSaleHadPriorityPlayer { get; set; }
		public PublicCompany ForcedStockSaleCompany { get; set; }

        public void PerformForcedStockSale(int nForceSaleAmount, PublicCompany company)
        {
            string sMsg;

            ForcedStockSalePending      = true;
            ForcedStockSaleCompleted    = false;
            ForcedStockSaleFullfilled   = false;
            ForcedStockSaleAmountNeeded = nForceSaleAmount;
            ForcedStockSaleAmountSold   = 0;
            ForcedStockSalePlayer       = company.Owner;
            ForcedStockSaleCompany		= company;
			//ForcedStockSalePlayerPriority = company.Owner.Priority;
			// switch priority to forced-seller player (works if seller already has SR priority too)
			ForcedStockSaleHadPriorityPlayer = null;
			foreach (Player p in Players)
			{
				if (p.Priority)
				{
					ForcedStockSaleHadPriorityPlayer = p;
					ForcedStockSaleHadPriorityPlayer.Priority = false;
					break;
				}
			}
			ForcedStockSalePlayer.Priority = true;


            sMsg = "Forced stock sale is beginning for " + ForcedStockSalePlayer.Name;
            History.Add(sMsg);

            sMsg = ForcedStockSalePlayer.Name + " must raise $" + ForcedStockSaleAmountNeeded.ToString() + " or go bankrupt!";
            History.Add(sMsg);

            GoToStockRound = true;
            
            UpdateAllPlayers();
            
            // redirect to stock round
            HttpContext.Current.Server.Transfer("StockRound.aspx");
        }

        public void SetNextCompanyToOperate()
        {
            CurrentlyOperatingCompany = null;
            OperatingRoundPhase = eOperatingRoundPhase.LayTile;
            TileAlreadyPlayed = false;

            int nCol;
            int nValue;
            int nMaxValue = 0;
            int nMaxCol = 0;
            int nRunningOrder = 99;
            char sRow;

            foreach (PublicCompany company in PublicCompanies)
            {
                if (company.Open && company.CanOperate && company.CompanyAvailable && !company.OperatedThisOperatingRound)
                {
                    nValue = company.StockMarketPosition.Value;
                    company.StockMarketPosition.GetRowAndColumnParts(out sRow, out nCol);

                    if (nValue >= nMaxValue)							// found higher priced stock
                    {
                        if (nValue > nMaxValue)							// reset col since new max value
                            nMaxCol = 0;

                        if (nCol >= nMaxCol)							// found same or higher further to the right
                        {
                            if (nCol > nMaxCol)							// reset running order since new col
                                nRunningOrder = 99;

                            if (company.RunningOrder <= nRunningOrder)	// same cell but higher order
                            {
                                nMaxValue = nValue;
                                nMaxCol = nCol;
                                nRunningOrder = company.RunningOrder;

                                CurrentlyOperatingCompany = company;
                            }
                        }
                    }
                }
            }

            // check if any companies are closed?
            CheckForClosedCompanies();

            if (CurrentlyOperatingCompany != null)
            {
                string sPlayer;
                string sHistoryMessage;

                sPlayer = CurrentOperatingOwner();
                sHistoryMessage = sPlayer + " is running " + this.CurrentlyOperatingCompany.Name;
                History.Add(sHistoryMessage);
                SaveGame ("");
            }
        }

        public bool OperatingRoundBeingInitialized
        {
            get { return _operatingRoundBeingInitialized; }
            set { _operatingRoundBeingInitialized = value; }
        }

        public Tiles Tiles
        {
            get { return _aTiles; }
			set { _aTiles = value; }
        }

        //public GameboardHexCollection GameboardHexes
        //{
        //    get { return _aGameboardHexes; }
		//	set { _aGameboardHexes = value; }
        //}

		public Gameboard Gameboard;

        // private company sale info
        public Player PrivateCompanyBuyer
        {
            get
            {
                return _privateCompanyBuyer;
            }
            set
            {
                _privateCompanyBuyer = value;
            }
        }

        public Player PrivateCompanySeller
        {
            get
            {
                return _privateCompanySeller;
            }
            set
            {
                _privateCompanySeller = value;
            }
        }

        public PublicCompany PrivateCompanyBuyingCompany
        {
            get
            {
                return _privateCompanyBuyingCompany;
            }
            set
            {
                _privateCompanyBuyingCompany = value;
            }
        }

        public PrivateCompany PrivateCompanyForSale
        {
            get
            {
                return _privateCompanyForSale;
            }
            set
            {
                _privateCompanyForSale = value;
            }
        }

        public int PrivateCompanySalePrice
        {
            get
            {
                return _privateCompanySalePrice;
            }
            set
            {
                _privateCompanySalePrice = value;
            }
        }

        public bool PrivateCompanySale
        {
            get
            {
                return _privateCompanySale;
            }
            set
            {
                _privateCompanySale = value;
            }
        }

        public bool PrivateCompanySold
        {
            get
            {
                return _privateCompanySold;
            }
            set
            {
                _privateCompanySold = value;
            }
        }

        public bool PrivateCompanySalePending
        {
            get
            {
                return _privateCompanySalePending;
            }
            set
            {
                _privateCompanySalePending = value;
            }
        }

        public string PrivateCompanyPriorityPlayerName
        {
            get
            {
                return _privateCompanyPriorityPlayerName;
            }
            set
            {
                _privateCompanyPriorityPlayerName = value;
            }
        }

        public void ResetPrivateSaleInfo()
        {
            PrivateCompanySale = false;
            PrivateCompanySold = false;
            PrivateCompanySalePrice = 0;
            PrivateCompanyBuyer = null;
            PrivateCompanySeller = null;
            PrivateCompanyForSale = null;
            PrivateCompanySalePending = false;
            PrivateCompanyBuyingCompany = null;
            PrivateCompanyPriorityPlayerName = "";
        }


        // existing train sale info
        public Player ExistingTrainBuyer
        {
            get
            {
                return _existingTrainBuyer;
            }
            set
            {
                _existingTrainBuyer = value;
            }
        }

        public Player ExistingTrainSeller
        {
            get
            {
                return _existingTrainSeller;
            }
            set
            {
                _existingTrainSeller = value;
            }
        }

        public PublicCompany ExistingTrainBuyingCompany
        {
            get
            {
                return _existingTrainBuyingCompany;
            }
            set
            {
                _existingTrainBuyingCompany = value;
            }
        }

        public PublicCompany ExistingTrainSellingCompany
        {
            get
            {
                return _existingTrainSellingCompany;
            }
            set
            {
                _existingTrainSellingCompany = value;
            }
        }

        public int ExistingTrainPurchasePrice
        {
            get
            {
                return _existingTrainPurchasePrice;
            }
            set
            {
                _existingTrainPurchasePrice = value;
            }
        }

        public bool ExistingTrainPurchaseSale
        {
            get
            {
                return _existingTrainPurchaseSale;
            }
            set
            {
                _existingTrainPurchaseSale = value;
            }
        }

        public bool ExistingTrainPurchased
        {
            get
            {
                return _existingTrainPurchased;
            }
            set
            {
                _existingTrainPurchased = value;
            }
        }

        public bool ExistingTrainSalePending
        {
            get
            {
                return _existingTrainSalePending;
            }
            set
            {
                _existingTrainSalePending = value;
            }
        }

        public string ExistingTrainToBuy
        {
            get
            {
                return _existingTrainToBuy;
            }
            set
            {
                _existingTrainToBuy = value;
            }
        }

        public string ExistingTrainPriorityPlayerName
        {
            get
            {
                return _existingTrainPriorityPlayerName;
            }
            set
            {
                _existingTrainPriorityPlayerName = value;
            }
        }

        public void ResetExistingTrainSaleInfo()
        {
            ExistingTrainPurchaseSale = false;
            ExistingTrainPurchased = false;
            ExistingTrainPurchasePrice = 0;
            ExistingTrainBuyer = null;
            ExistingTrainSeller = null;
            ExistingTrainSalePending = false;
            ExistingTrainBuyingCompany = null;
            ExistingTrainSellingCompany = null;
            ExistingTrainToBuy = "";
            ExistingTrainPriorityPlayerName = "";
        }


        // CGR formation info
        public string CGRFormationPlayerName
        {
            get { return _cgrFormationPlayerName; }
            set { _cgrFormationPlayerName = value; }
        }

        public string CGRFormationPriorityPlayerName
        {
            get { return _cgrFormationPriorityPlayerName; }
            set { _cgrFormationPriorityPlayerName = value; }
        }

        // general game info
        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }

        public int Bank
        {
            get
            {
                if (_bank < 0)
                    return 0;
                else
                    return _bank;
            }
            set
            {
                _bank = value;

                if (_bank <= 0)	// busted
                    BankBusted = true;
            }
        }

        public bool GameOver
        {
            get
            {
                return _gameOver;
            }
            set
            {
                _gameOver = value;
            }
        }

        public bool CGRFormed
        {
            get
            {
                return _cgrFormed;
            }
            set
            {
                _cgrFormed = value;
            }
        }

        public bool CGRForming
        {
            get
            {
                return _cgrForming;
            }
            set
            {
                _cgrForming = value;
            }
        }
        
        public bool BankBusted
        {
            get
            {
                return _bankBusted;
            }
            set
            {
                _bankBusted = value;
                
                if (History != null)
                {
                    if (History.Count > 0)
                    {
                        if (History[0] == "Stock Round")
                            NumberOfOperatingRoundsLeft = 3;	                        // busted in SR so do 3 more
                        else
                            NumberOfOperatingRoundsLeft = (3 - CurrentOperatingRound);  // busted in OR so finish this one and done
                    }
                }
            }
        }

        public bool BankruptcyOccurred
        {
            get
            {
                return _bankruptcyOccurred;
            }
            set
            {
                _bankruptcyOccurred = value;
            }
        }

        public int StartingCapital
        {
            get
            {
                return _startingCapital;
            }
            set
            {
                _startingCapital = value;
            }
        }

        public int CertificateLimit
        {
            get
            {
                return _certificateLimit;
            }
            set
            {
                _certificateLimit = value;
            }
        }


        public string AuctionPriorityPlayerName
        {
            get
            {
                return _auctionPriorityPlayerName;
            }
            set
            {
                _auctionPriorityPlayerName = value;
            }
        }

        public void PayoutPrivates()
        {
            string sPayout;

            foreach (Player player in this.Players)
            {
                foreach (PrivateCompany privateCompany in player.PrivateCompanies)
                {
                    player.Cash += privateCompany.Income;
                    player.AvailableCash += privateCompany.Income;
                    Bank -= privateCompany.Income;

                    sPayout = player.Name + " was paid $" + privateCompany.Income + " by " + privateCompany.Name; ;
                    History.Add(sPayout);
                }
            }

            foreach (PublicCompany publicCompany in this.PublicCompanies)
            {
                foreach (PrivateCompany privateCompany in publicCompany.PrivateCompanies)
                {
                    publicCompany.Cash += privateCompany.Income;
                    Bank -= privateCompany.Income;

                    sPayout = publicCompany.Name + " was paid $" + privateCompany.Income + " by " + privateCompany.Name; ;
                    History.Add(sPayout);
                }
            }
        }

        public int AuctionPhasePasses
        {
            get
            {
                return _auctionPhasePasses;
            }
            set
            {
                _auctionPhasePasses = value;
            }
        }

        public int Passes
        {
            get
            {
                return _passes;
            }
            set
            {
                _passes = value;
            }
        }

        public bool PrivateSold
        {
            get
            {
                return _privateSold;
            }
            set
            {
                _privateSold = value;
            }
        }

        public bool AuctionPhase
        {
            get
            {
                return _auctionPhase;
            }
            set
            {
                _auctionPhase = value;
            }
        }

        public bool FirstStockRound
        {
            get
            {
                return _firstStockRound;
            }
            set
            {
                _firstStockRound = value;
            }
        }

        public int TrainLimit
        {
            get
            {
                return _trainLimit;
            }
            set
            {
                _trainLimit = value;
            }
        }

        public bool MaxTrain8Train
        {
            get
            {
                return _maxTrain8Train;
            }
            set
            {
                _maxTrain8Train = value;
            }
        }

        public bool AllowUseOfOptional6Train
        {
            get
            {
                return _allowUseOfOptional6Train;
            }
            set
            {
                _allowUseOfOptional6Train = value;
            }
        }

        public bool CollusionAllowed
        {
            get
            {
                return _collusionAllowed;
            }
            set
            {
                _collusionAllowed = value;
            }
        }

        public int CostOfNextNewTrainAvailable
        {
            get
            {
                return _costOfNextNewTrainAvailable;
            }
            set
            {
                _costOfNextNewTrainAvailable = value;
            }
        }

        public int SharesNeededtoOpen
        {
            get
            {
                return _sharesNeededtoOpen;
            }
            set
            {
                _sharesNeededtoOpen = value;
            }
        }

        public int CurrentOperatingRound
        {
            get
            {
                return _currentOperatingRound;
            }
            set
            {
                _currentOperatingRound = value;
            }
        }

        public int NumberOfOperatingRounds
        {
            get
            {
                return _numberOfOperatingRoundsNow;
            }
            set
            {
                _numberOfOperatingRoundsNow = value;
            }
        }

        public int NumberOfOperatingRoundsNext
        {
            get
            {
                return _numberOfOperatingRoundsNext;
            }
            set
            {
                _numberOfOperatingRoundsNext = value;
            }
        }

        public int NumberOfOperatingRoundsLeft
        {
            get
            {
                return _numberOfOperatingRoundsLeft;
            }
            set
            {
                _numberOfOperatingRoundsLeft = value;
            }
        }

        public bool ViewGameStatusFromStockRound
        {
            get
            {
                return _viewGameStatusFromStockRound;
            }
            set
            {
                _viewGameStatusFromStockRound = value;
            }
        }

        public bool ViewStockMarketFromStockRound
        {
            get
            {
                return _viewStockMarketFromStockRound;
            }
            set
            {
                _viewStockMarketFromStockRound = value;
            }
        }

        public bool IsPermanantTrain(string sTrain)
        {
            bool bPermanant = false;

            if (sTrain == "5" || sTrain == "6" || sTrain == "8" || sTrain == "D")
                bPermanant = true;

            return bPermanant;
        }

        public BidCollection Bids
        {
            get { return _aBids; }
			set { _aBids = value; }
        }

        public void NextPrivatePurchased(string sPlayer)
        {
            int nCost = 0;
            _privateSold = true;

            foreach (Player player in Players)
            {
                if (sPlayer == player.Name)
                {
                    PrivateCompany privateCompany = this.PrivateCompanies[0];

                    if (privateCompany.LastBid != 0)
                        nCost = privateCompany.LastBid;  // JoeB would be happy.
                    else
                        nCost = privateCompany.Cost;

                    PrivateCompanies.Remove(privateCompany);

                    player.Cash -= nCost;
                    Bank += nCost;

                    player.AvailableCash -= nCost;
                    player.PrivateCompanies.Add(privateCompany);
                    string sBuyCompany;
                    sBuyCompany = player.Name + " bought " + privateCompany.NickName + " for $" + privateCompany.Cost;
                    this.History.Add(sBuyCompany);

                    if (privateCompany.Cost < 20)	// Reset cost to at least $20.
                    {
                        privateCompany.Cost = 20;
                    }
                    break;
                }
            }
        }


        public bool Started
        {
            get
            {
                return started;
            }
            set
            {
                if (started != value)
                {
                    started = value;
                }
            }
        }

        /// <summary>
        /// Save the current game. 
        /// </summary>
        /// <param name="uniqueFolder">
        /// This is an optional name that if present is used as part of the path. 
        /// This allows the caller create a unique key for each game that is started to avoid
        /// overwriting previous games if an existing game name is used.
        /// If this is not supplied then the files are stored in the base file path.
        /// </param>
        public void SaveGame(string uniqueFolder)
        {
			// the assembly will need to be trusted with FileIO permission read/write to this directory!
            // Calculate the directory path, create it if needed
			string directory = Path.Combine( Path.Combine( _baseFilePath, "SavedGames" ), GameName );
            if ( !string.IsNullOrEmpty( uniqueFolder ) )
                directory = Path.Combine( directory, uniqueFolder );

			try
			{
                Directory.CreateDirectory( directory );
                string fullPath;

#if USE_BINARY_SERIALIZER
                fullPath = FileUtils.GetNextFileName( directory, GameName, "DAT" );
				BinaryFormatter formatter = new BinaryFormatter();
                using ( FileStream stream = new FileStream( fullPath, FileMode.Create ) )
				{
					formatter.Serialize( stream, this );
					stream.Close();
                }
#else   //  USE_BINARY_SERIALIZER
#if UseSoapFormatter
                fullPath = FileUtils.GetNextFileName( directory, GameName, "XML" );
				// SoapFormatter solves the circular reference problems
				SoapFormatter formatter = new SoapFormatter();
#if Use_VersionTolerant_Serializer
				// full type info so that when deserialized all needed conversions are known 
				formatter.TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways;
#endif
				using (FileStream stream = new FileStream(fullPath , FileMode.Create))
				{
					formatter.Serialize(stream, this);
					stream.Close();
				}
#else   //  UseSoapFormatter
                // use xml serializer
                fullPath = FileUtils.GetNextFileName( directory, GameName, "XML" );
				XmlSerializer mySerializer = new XmlSerializer(typeof(Game));
				using (StreamWriter myWriter = new StreamWriter(fullPath ))
				{
					mySerializer.Serialize(myWriter, this);
					myWriter.Close();
				}
#endif  //  UseSoapFormatter
#endif  //  USE_BINARY_SERIALIZER
                PathOfLastSavedGame = fullPath;
                return;
			}
			catch (Exception e)  // saving of the game failed, out of disk space, permissions?
			{
                System.Diagnostics.Trace.WriteLine( e.ToString() );
				History.Add("Unable to save game: . " + e.Message);
				History.Add("Path: . " + PathOfLastSavedGame);
			}
        }   //  SaveGame

		static public Game LoadGame(string sFullPath)
		{
			Game restoredGame;
#if USE_BINARY_SERIALIZER
			BinaryFormatter mySerializer = new BinaryFormatter();
#else
#if UseSoapFormatter
			// SoapFormatter solves the circular reference problems
			SoapFormatter mySerializer = new SoapFormatter();
#else
			XmlSerializer mySerializer = new XmlSerializer(typeof(Game));
#endif
#endif
#if Use_VersionTolerant_Serializer
			SerializationUtils.VersionTolerantSerializer vts = new SerializationUtils.VersionTolerantSerializer();

			// Tell the serializer which types (classes) to handle; add more types as needed
			vts.AddType(typeof(Game));
			//vts.AddType( typeof( PublicCompany ) );
			vts.AddType( typeof( PublicCompanies ) );
			vts.AddType( typeof( SegmentEndpoint ) );

			mySerializer.SurrogateSelector = vts.Selector;
#endif
			using (FileStream fileStream = new FileStream(sFullPath, FileMode.Open))
			{
				restoredGame = (Game)mySerializer.Deserialize(fileStream);
				fileStream.Close();
			}

			return restoredGame;
		}

        public bool ExercisingTheCanadaCompany
        {
            get { return _exercisingTheCanadaCompany; }
            set { _exercisingTheCanadaCompany = value; }
        }

        public bool ExercisingWaterlooTileLay
        {
            get { return _exercisingWaterlooTileLay; }
            set { _exercisingWaterlooTileLay = value; }
        }

        public bool ExercisingWaterlooTokenPlacement
        {
            get { return _exercisingWaterlooTokenPlacement; }
            set { _exercisingWaterlooTokenPlacement = value; }
        }

        public bool ExercisingPort
        {
            get { return _exercisingPort; }
            set { _exercisingPort = value; }
        }

        public Game() // Default constructor, needed for serialization.
        {
        }

        public Game(string MachineName, string baseFilePath) // Default constructor
        {
            CgrOptionalTokenReplacement = false;
            _MachineName = MachineName;
            _baseFilePath = baseFilePath;
            _tunnelsForSale = 2;	// Total of 3, 1 automatically comes with the private.
            _bridgesForSale = 2;	// Total of 3, 1 automatically comes with the private.
            _exercisingTheCanadaCompany = false;
            _exercisingWaterlooTileLay = false;
            _exercisingWaterlooTokenPlacement = false;
            _exercisingPort = false;
            _companyMustPlaceInitialToken = false;
            //			_globalHexHandler = null;
            _currentlyOperatingOwner = "";
            _currentlyOperatingCompany = null;
            _aTiles = new Tiles();
            //_aGameboardHexes = new GameboardHexCollection();
			Gameboard = new Gameboard();

            _aPrivateCompanies = new PrivateCompanies();
            _aPublicCompanies = new PublicCompanies();
            _aPlayers = new PlayerCollection();
            _aBids = new BidCollection();
            _aCurrentInAuctionBids = new BidCollection();
            _history = new StringCollection();

            _a2Trains = new TrainCollection();
            _a3Trains = new TrainCollection();
            _a4Trains = new TrainCollection();
            _a5Trains = new TrainCollection();
            _a6Trains = new TrainCollection();
            _a8Trains = new TrainCollection();
            _aDTrains = new TrainCollection();
            _aOpenMarketTrains = new TrainCollection();

			_htGamePhaseInfo = new Hashtable(); //MyDictionary<string, GamePhaseInfo>();
            _aStockMarketPositions = new StockMarketPositionCollection();

            started = false;

            Parse18xxConfigFile();

            _gamePhase = eGamePhase.Phase2;
            _operatingRoundPhase = eOperatingRoundPhase.LayTile;

            nGameSavedNumber = 0;
            _firstStockRound = true;
            _auctionPhase = false;
            _auctionPhasePasses = 0;
            _passes = 0;
            _privateSold = false;

            GameOver = false;
            CGRFormed = false;
            CGRForming = false;
            Discard4s = false;
            BankBusted = false;
            BankruptcyOccurred = false;
            CertificateLimit = 30;	// TODO set to correct value
            ViewGameStatusFromStockRound = true;
            ViewStockMarketFromStockRound = true;

            TrainLimit = 4;
            MaxTrain8Train = true;		// default to 8 trains
            CollusionAllowed = false;	// allow players to collude with each other? (i.e. buy trains from each other)
            SharesNeededtoOpen = 2;
            CostOfNextNewTrainAvailable = 100;		// cost of a 2 train
            CanBuyPrivateCompanies = false;
            YellowTilesAvailable = true;
            GreenTilesAvailable = false;
            BrownTilesAvailable = false;
            GrayTilesAvailable = false;	
            CurrentOperatingRound = 0;
            NumberOfOperatingRounds = 1;
            NumberOfOperatingRoundsNext = 1;
            NumberOfOperatingRoundsLeft = 1;	// default to busting in OR, else 3

            CGRFormationPlayerName = "";
            CGRFormationPriorityPlayerName = "";

            ResetPrivateSaleInfo();
            ResetExistingTrainSaleInfo();

            AdjustGamePhaseInfo("2", "");	// default to 2 train settings

            // forced stock sale stuff
            ForcedStockSalePending = false;
            ForcedStockSaleCompleted = true;
            ForcedStockSaleFullfilled = true;
            ForcedStockSalePlayer = null;
            ForcedStockSaleCompany = null;
            ForcedStockSaleAmountNeeded = 0;
            ForcedStockSaleAmountSold = 0;

            // JoeB - initialize track segments for initial trackage
			foreach (GameboardHex hex in Gameboard.GameboardHexes)
                TrackSegment.CalculateHexRoutes(hex);
        }

		public bool AddPlayer(string sPlayer, bool isAiPlayer, ref string sError)
        {
            if (this.RejoinedPlayers != null) // Restarting a game?
            {
                if (this.RejoinedPlayers.Contains(sPlayer))
                {
                    sError = "Can't join game because the restored game already has a player with this name!";
                    return true;
                }

                string sValidNames = "";
                bool bFirst = true;

                foreach (Player player in this.Players)
                {
                    if (player.Name.Equals(sPlayer))
                    {
                        return false;  // success
                    }
                    else
                    {
                        if (!this.RejoinedPlayers.Contains(player.Name))
                        {
                            if (!bFirst)
                            {
                                sValidNames += ", ";
                            }
                            else
                            {
                                bFirst = false;
                            }

                            sValidNames += player.Name;
                        }
                    }
                }

                sError = "Can't join the restored game because a player with your name doesn't exist.  Possible player names are: " + sValidNames;
                return true;
            }
            
            if (this.Started)
            {
                sError = "Can't add player because the game has already started!";
                return true;
            }

            sPlayer = sPlayer.Trim();

            if (sPlayer.Length < 1)
            {
                sError = "Can't add player because player name is blank!";
                return true;
            }

            foreach (Player player in Players)
            {
                if (player.Name == sPlayer)
                {
                    sError = "Can't add player because a player with that name already exists!";
                    return true;
                }
            }

            Player newPlayer;
			if (isAiPlayer)
				newPlayer = new AiPlayer(sPlayer);
			else
				newPlayer = new Player(sPlayer);

            newPlayer.Game = this;
            Players.Add(newPlayer);
            return false;
        }


        // collection properties
        public BidCollection CurrentInAuctionBids
        {
            get { return _aCurrentInAuctionBids; }
			set { _aCurrentInAuctionBids = value; }
        }

        public StringCollection History
        {
            get { return _history; }
			set { _history = value; }
        }

        public StringCollection RejoinedPlayers
        {
            get { return _rejoinedPlayers; }
            set { _rejoinedPlayers = value; }
        }


        public PrivateCompanies PrivateCompanies
        {
            get { return _aPrivateCompanies; }
			set { _aPrivateCompanies = value; }
        }

        public PublicCompanies PublicCompanies
        {
            get { return _aPublicCompanies; }
			set { _aPublicCompanies = value; }
        }

        public PlayerCollection Players
        {
            get { return _aPlayers; }
			set { _aPlayers = value; }
        }

        public StockMarketPositionCollection StockMarketPositions
        {
            get { return _aStockMarketPositions; }
			set { _aStockMarketPositions = value; }
        }

		public TrainCollection Trains_2
		{
			get { return _a2Trains; }
			set { _a2Trains = value; }
		}

        public TrainCollection Trains_3
        {
            get { return _a3Trains; }
			set { _a3Trains = value; }
        }

        public TrainCollection Trains_4
        {
            get { return _a4Trains; }
			set { _a4Trains = value; }
        }

        public TrainCollection Trains_5
        {
            get { return _a5Trains; }
			set { _a5Trains = value; }
        }

        public TrainCollection Trains_6
        {
            get { return _a6Trains; }
			set { _a6Trains = value; }
        }

        public TrainCollection Trains_8
        {
            get { return _a8Trains; }
			set { _a8Trains = value; }
        }

        public TrainCollection Trains_D
        {
            get { return _aDTrains; }
			set { _aDTrains = value; }
        }

        public TrainCollection Trains_OpenMarket
        {
            get { return _aOpenMarketTrains; }
			set { _aOpenMarketTrains = value; }
        }

        public TrainCollection GetTrainCollectionFromType(string sTrain)
        {
            TrainCollection trains = null;

            switch (sTrain)
            {
                case "2":
                    trains = Trains_2;
                    break;

                case "3":
                    trains = Trains_3;
                    break;

                case "4":
                    trains = Trains_4;
                    break;

                case "5":
                    trains = Trains_5;
                    break;

                case "6":
                    trains = Trains_6;
                    break;

                case "8":
                    trains = Trains_8;
                    break;

                case "D":
                    trains = Trains_D;
                    break;
            }

            return trains;
        }

        // starts from 2 and goes to 6/8/D to find what trains are available
        // this function is used to tell how many shares we need to buy to open/run
        public void UpdateSharesNeededToOpenOrRunBasedOnCurrentlyAvailableTrain()
        {
            int nSharesNeeded = 6;

            if (Trains_2.Count > 0)
                nSharesNeeded = 2;
            else if (Trains_3.Count > 0)
                nSharesNeeded = 3;
            else if (Trains_4.Count > 0)
                nSharesNeeded = 4;
            else if (Trains_5.Count > 0)
                nSharesNeeded = 5;

            SharesNeededtoOpen = nSharesNeeded;
        }

        // JoeB: safe to serialize - serializes properly
        public Hashtable /*MyDictionary<string, GamePhaseInfo>*/ GamePhasesInfo
        {
            get { return _htGamePhaseInfo; }
			set { _htGamePhaseInfo = value; }	// JoeB: needed for LoadGame serialization
        }

        // xml parsing code
        private void Parse18xxConfigFile()
        {
            //string		sFile = "file://localhost/1856/1856.xml"; // "C:\\Inetpub\\wwwroot\\1856\\1856.xml";	// TODO fix this so not hardcoded
            //string		sFile = "http://localhost/1856/1856.xml"; // "C:\\Inetpub\\wwwroot\\1856\\1856.xml";	// TODO fix this so not hardcoded
            //string		sFile = "http://" + _MachineName + "/1856/1856.xml"; // "C:\\Inetpub\\wwwroot\\1856\\1856.xml";	// TODO fix this so not hardcoded
            //string sFile = "C:\\Inetpub\\wwwroot\\1856\\1856.xml";	// TODO fix this so not hardcoded

			// JoeB: MapPath is apparently the approved way to get to the root path of the web project at runtime
            string sFile = System.Web.Hosting.HostingEnvironment.MapPath("~") + "\\1856.xml";  // JWF 11.24.2007
//            string sFile = System.Web.Hosting.HostingEnvironment.MapPath("~") + "1856.xml";
            XmlDocument doc = new XmlDocument();

            doc.Load(sFile);

            foreach (XmlNode node in doc.GetElementsByTagName("GameInfo"))
            {
                Name = node["Name"].InnerText;
                
                int nBank = 0;
                bool bBank = int.TryParse(node["Bank"].InnerText, out nBank);
                Bank = nBank;

                int nStartingCapital = 0;
                bool bStartingCapital = int.TryParse(node["StartingCapital"].InnerText, out nStartingCapital);
                StartingCapital = nStartingCapital;

                int nPublicCompanies = 0;
                bool bPublicCompanies = int.TryParse(node["PublicCompanies"].InnerText, out nPublicCompanies);
                NumberPublicCompanies = nPublicCompanies;
            }

            Bank -= StartingCapital;

            foreach (XmlNode node in doc.GetElementsByTagName("PrivateCompany"))
                ParsePrivateCompanyInfo(node);

            foreach (XmlNode node in doc.GetElementsByTagName("PublicCompany"))
                ParsePublicCompanyInfo(node);

            foreach (XmlNode node in doc.GetElementsByTagName("StockMarketPosition"))
                ParseStockMarketPositionInfo(node);

            foreach (XmlNode node in doc.GetElementsByTagName("GameBoardHex"))
                ParseGameHexInfo(node);

            foreach (XmlNode node in doc.GetElementsByTagName("GameBoardTile"))
                ParseGameTileInfo(node);

            foreach (XmlNode node in doc.GetElementsByTagName("Train"))
                ParseTrainInfo(node);

            foreach (XmlNode node in doc.GetElementsByTagName("Phase"))
                ParseGamePhaseInfo(node);
        }

        private void ParseGameHexInfo(XmlNode node)
        {
            // get Hexes basic info
            string sLocation = node.Attributes["Location"].Value;
            GameboardHex hex = new GameboardHex(sLocation);

            if (node.Attributes["City"] != null)
            {
                hex.City = node.Attributes["City"].Value;
            }

            if (!node["Info"].IsEmpty)
            {
                bool bReturn;
                
                foreach (XmlNode info in node["Info"].ChildNodes)
                {
                    // convert the Hex type from text to enum
                    switch (info.Name)
                    {
                        case "Cost":
                            int nCost = 0;
                            bReturn = int.TryParse(info.InnerText, out nCost);
                            hex.Cost = nCost;
                            break;

                        case "MaxTokens":
                            int nMaxTokens = 0;
                            bReturn = int.TryParse(info.InnerText, out nMaxTokens);
                            hex.MaxTokens  = nMaxTokens;
                            break;

                        case "InitValue":
                            int nInitValue = 0;
                            bReturn = int.TryParse(info.InnerText, out nInitValue);
                            hex.InitValue = nInitValue;
                            break;

                        case "Values":
                            hex.Values = info.InnerText;
                            string sFirstValue;
                            int nIndex;

                            nIndex = hex.Values.IndexOf(',');

                            if (nIndex > 0)
                            {
                                sFirstValue = hex.Values.Substring(0, nIndex);
                                nInitValue = 0;
                                bReturn = int.TryParse(sFirstValue, out nInitValue);	// Gets the first value.
                                hex.InitValue = nInitValue;	// Gets the first value.
                                
                            }
                            break;

                        case "Town":
                            // convert the town type from text to enum
                            switch (info.InnerText)
                            {
                                case "City":
                                    hex.TownType = GameboardHex.eTownType.City;
                                    break;

                                case "Town":
                                    hex.TownType = GameboardHex.eTownType.Town;
                                    break;

                                case "DblTown":
                                    hex.TownType = GameboardHex.eTownType.DblTown;
                                    break;
                            }

                            break;

                        case "Type":
                            // convert the Hex type from text to enum
                            switch (info.InnerText)
                            {
                                case "OffBoard":
                                    hex.HexType = GameboardHex.eHexType.OffBoard;
                                    break;

                                case "PassThru":
                                    hex.HexType = GameboardHex.eHexType.PassThru;
                                    break;

                                case "NotUpgradable":
                                    hex.HexType = GameboardHex.eHexType.NotUpgradable;
                                    break;
                            }

                            break;
                    }
                }
            }

            // get Hexs starting attributes
            if (!node["Attributes"].IsEmpty)
            {
                int nAttributes = 0;

                foreach (XmlNode attributes in node["Attributes"].ChildNodes)
                {
                    // convert the Hex type from text to enum
                    switch (attributes.Name)
                    {
                        case "L":
                            nAttributes |= (int)GameboardHex.eHexAttributes.L;
                            break;

                        case "Port":
                            nAttributes |= (int)GameboardHex.eHexAttributes.Port;
                            break;

                        case "Bridge":
                            nAttributes |= (int)GameboardHex.eHexAttributes.Bridge;
                            break;

                        case "Tunnel":
                            nAttributes |= (int)GameboardHex.eHexAttributes.Tunnel;
                            break;

                        case "Private":
                            nAttributes |= (int)GameboardHex.eHexAttributes.Private;
                            hex.PrivateCompanyAtThisHex = attributes.InnerText;
                            break;

                        case "InitialColor":	// So far only yellow hexes in 1856.
                            hex.InitialColor = Color.Yellow;
                            break;

                        case "InitialTile":
                            hex.InitialTile = attributes.InnerText;
                            break;

                        case "FinalTile":
                            hex.FinalTile = attributes.InnerText;
                            break;

                        case "SeparateCities":	// 59 and Toronto in 1856.
                            hex.SeperateCities = true;
                            break;
                        
                        case "ContainsBypass":
                            hex.HexType = GameboardHex.eHexType.ContainsBypass;
                            break;
                    }
                }

                hex.Attributes = nAttributes;
            }

            // get Hexs starting neighbors
            if (!node["Neighbors"].IsEmpty)
            {
                foreach (XmlNode neighbor in node["Neighbors"].ChildNodes)
                {
                    if (neighbor.InnerText.Length > 0)
                    {
                        hex.AddNeighbors(neighbor.Name, neighbor.InnerText);
                    }
                }
            }

            string[] asEndRoutes = new string[6];

            // get Hexs starting routes
            if (!node["InitRoutes"].IsEmpty)
            {
                foreach (XmlNode routes in node["InitRoutes"].ChildNodes)
                {
                    //					if (routes.InnerText.Length > 0)
                    //					{
                    Side startSide = new Side();
                    SideCollection endSides = new SideCollection();
                    Side endSide;

                    startSide.SetDirection(routes.Name);
                    string sEndRoutes = routes.InnerText;

                    asEndRoutes = sEndRoutes.Split(',');

                    foreach (string sEndRoute in asEndRoutes)
                    {
                        string sEnd;
                        sEnd = sEndRoute.Trim();

                        if (sEnd.Length > 0)
                        {
                            endSide = new Side();
                            endSide.SetDirection(sEnd);
                            endSides.Add(endSide);
                        }
                    }

                    hex.ExistingRoutes.Add(startSide, endSides);
                    //					}
                }
            }

            hex.Game = this;
			Gameboard.GameboardHexes.Add(hex);
        }

        private void ParseTrainInfo(XmlNode node)
        {
            int nCost;
            int nQuantity;
            int nTradeTo8Cost;
            int nTradeToDCost;
            int nStops;
            string sCost;
            string sName;
            string sRusts;
            bool bReturn;

            sName = node.Attributes["Name"].Value;
            bReturn = int.TryParse(node.Attributes["Qty"].Value, out nQuantity);
            bReturn = int.TryParse(node.Attributes["Cost"].Value, out nCost);
            sRusts = node.Attributes["Rusts"].Value;
            sCost = node.Attributes["TradeUp8"].Value;

            if (sCost.Length > 0)
                bReturn = int.TryParse(sCost, out nTradeTo8Cost);
            else
                nTradeTo8Cost = 0;

            sCost = node.Attributes["TradeUpD"].Value;

            if (sCost.Length > 0)
                bReturn = int.TryParse(sCost, out nTradeToDCost);
            else
                nTradeToDCost = 0;

            nStops = 0;
            if (node.Attributes["Stops"] != null)
                int.TryParse(node.Attributes["Stops"].Value, out nStops);

            for (int i = 0; i < nQuantity; i++)
            {
                Train train = new Train();

                train.Name = sName;
                train.Quantity = nQuantity;
                train.Cost = nCost;
                train.Rusts = sRusts;
                train.Stops = nStops;
                train.TradeUpTo8Cost = nTradeTo8Cost;
                train.TradeUpToDCost = nTradeToDCost;

                switch (sName)
                {
                    case "2":
                        Trains_2.Add(train);
                        break;

                    case "3":
                        Trains_3.Add(train);
                        break;

                    case "4":
                        Trains_4.Add(train);
                        break;

                    case "5":
                        Trains_5.Add(train);
                        break;

                    case "6":
                        Trains_6.Add(train);
                        break;

                    case "8":
                        Trains_8.Add(train);
                        break;

                    case "D":
						train.UnlimitedStops = true;
                        Trains_D.Add(train);
                        break;
                }
            }
        }

        private void ParseGameTileInfo(XmlNode node)
        {
            // get Tiles basic info
            string sUpgrade = "";
            string sDowngrade = "";
            string sName = node.Attributes["Number"].Value;
            int nQuantity = 0;
            bool bReturn = int.TryParse(node.Attributes["Qty"].Value, out nQuantity);
            string sColor = node.Attributes["Color"].Value;
            string sFile = node.Attributes["Image"].Value;
            string sTownType = "Unpopulated";
			GameboardHex.eTownType eTileTownType = GameboardHex.eTownType.None;
            Tile.eTileColor eColor = Tile.eTileColor.Yellow;
            int nWorth = 0;	//  Value, for example, yellow cities are worth 20 in 1856.

            if (node.Attributes["Town"] != null)
            {
                sTownType = node.Attributes["Town"].Value;
            }

            if (node.Attributes["Value"] != null)
            {
                bReturn = int.TryParse(node.Attributes["Value"].Value, out nWorth);
            }

            switch (sTownType)
            {
                case "Unpopulated":
                    {
                        eTileTownType = GameboardHex.eTownType.None;
                    }
                    break;

                case "City":
                    {
						eTileTownType = GameboardHex.eTownType.City;
                    }
                    break;

                case "Town":
                    {
						eTileTownType = GameboardHex.eTownType.Town;
                    }
                    break;

                case "DblTown":
                    {
						eTileTownType = GameboardHex.eTownType.DblTown;
                    }
                    break;
            }

            switch (sColor)
            {
                case "Yellow":
                    eColor = Tile.eTileColor.Yellow;
                    break;

                case "Green":
                    eColor = Tile.eTileColor.Green;
                    break;

                case "Brown":
                    eColor = Tile.eTileColor.Brown;
                    break;

                case "Gray":
                    eColor = Tile.eTileColor.Gray;
                    break;
            }

            Tile tile = new Tile(sName, nQuantity, eColor, sFile);

            tile.Worth = nWorth;
            tile.TownType = eTileTownType;

            string[] asEndRoutes = new string[6];

            // get tiles starting routes
            if (!node["Routes"].IsEmpty)
            {
                foreach (XmlNode routes in node["Routes"].ChildNodes)
                {
                    Side startSide = new Side();
                    SideCollection endSides = new SideCollection();
                    Side endSide;

                    startSide.SetDirection(routes.Name);
                    string sEndRoutes = routes.InnerText;

                    asEndRoutes = sEndRoutes.Split(',');

                    foreach (string sEndRoute in asEndRoutes)
                    {
                        string sEnd = sEndRoute.Trim();

                        if (sEnd.Length > 0)	// Tile 59 will not have any end routes.
                        {
                            endSide = new Side();
                            endSide.SetDirection(sEnd);
                            endSides.Add(endSide);
                        }
                    }

                    tile.Routes.Add(startSide, endSides);
                }
            }

            if (!node["TileUpgrades"].IsEmpty)
            {
                foreach (XmlNode upgrade in node["TileUpgrades"].ChildNodes)
                {
                    if (upgrade.InnerText.Length > 0)
                    {
                        sUpgrade = upgrade.InnerText;
                        tile.ValidUpgrades.Add(sUpgrade);
                    }
                }
            }

            if (!node["TileDowngrades"].IsEmpty)
            {
                foreach (XmlNode downgrade in node["TileDowngrades"].ChildNodes)
                {
                    if (downgrade.InnerText.Length > 0)
                    {
                        sDowngrade = downgrade.InnerText;
                        tile.ValidDowngrades.Add(sDowngrade);
                    }
                }
            }

            if (node.Attributes["NoNewTrack"] != null)
            {
                tile.UpgradeHasNewTrack = false;	// Toronto in 1856 and New York in 1830.  Any others?  Paris in 1826 maybe???
            }

            if (node.Attributes["Name"] != null)
            {
                tile.TileName = node.Attributes["Name"].Value;
            }

            _aTiles.Add(tile);
        }

        private void ParseGamePhaseInfo(XmlNode node)
        {
            int nTrainLimit;
            int nOffBoardValue;
            int nOperatingRounds;
            string sPhase;
            string sTilesAvailable;

            sPhase = node.Attributes["Number"].Value;
            bool bReturn = int.TryParse(node.Attributes["TrainLimit"].Value, out nTrainLimit);
            sTilesAvailable = node.Attributes["TilesAvailable"].Value;
            bReturn = int.TryParse(node.Attributes["OffBoardValue"].Value, out nOffBoardValue);
            bReturn = int.TryParse(node.Attributes["OperatingRounds"].Value, out nOperatingRounds);

            GamePhaseInfo phaseInfo = new GamePhaseInfo();

            phaseInfo.Phase = sPhase;
            phaseInfo.TrainLimit = nTrainLimit;
            phaseInfo.TilesAvailable = sTilesAvailable;
            phaseInfo.OffBoardValue = nOffBoardValue;
            phaseInfo.OperatingRounds = nOperatingRounds;

            GamePhasesInfo.Add (sPhase, phaseInfo);
        }

        private void ParsePublicCompanyInfo(XmlNode node)
        {
            int nIndex;
            int nTokens;
            string sName;
            string sHome;
            string sDestination;
            bool bReturn;

            sName = node.Attributes["Name"].Value;
            sHome = node.Attributes["Home"].Value;
            sDestination = node.Attributes["Dest"].Value;
            bReturn = int.TryParse(node.Attributes["Tokens"].Value, out nTokens);
            //nIndex = int.Parse(node.Attributes["Index"].Value);
            bReturn = int.TryParse(node.Attributes["Index"].Value, out nIndex);

            PublicCompany publicCompany = new PublicCompany();

            if (node.Attributes["Side"] != null)
            {
                Side side = publicCompany.StartSide;

                side.SetDirection(node.Attributes["Side"].Value);
                publicCompany.StartSideSpecified = true;
            }

            publicCompany.Game = this;
            publicCompany.Name = sName;
            publicCompany.Home = sHome;
            publicCompany.Destination = sDestination;		// Bob bug!!!!  .Home should be .Destination
            publicCompany.Tokens = nTokens;
            publicCompany.Index = nIndex;
            this._aPublicCompanies.Add(publicCompany);

            if (sName != "CGR")		// TODO must handle the CGR share creation at a later place
            {
                Share share = new Share(20, publicCompany, true);	// Add the presidency.
                publicCompany.IPOShares.Add(share);

                for (int i = 2; i <= 9; i++)	// Add the shares into the IPO, later read this info from the config file.  9 shares, 1 is the presidency.
                {
                    share = new Share(10, publicCompany, false);
                    publicCompany.IPOShares.Add(share);
                }
            }
        }

        private void ParsePrivateCompanyInfo(XmlNode node)
        {
            int nCost;
            int nIncome;
            string sHome;
            string sName;
            string sNickName;
            bool bReturn;

            sName = node.Attributes["Name"].Value;
            sNickName = node.Attributes["Nickname"].Value;
            bReturn = int.TryParse(node.Attributes["Cost"].Value, out nCost);
            bReturn = int.TryParse(node.Attributes["Income"].Value, out nIncome);
            sHome = node.Attributes["Home"].Value;

            PrivateCompany privateCompany = new PrivateCompany();
            privateCompany.Cost = nCost;
            privateCompany.Name = sName;
            privateCompany.NickName = sNickName;
            privateCompany.Income = nIncome;
            privateCompany.Home = sHome;
            PrivateCompanies.Add(privateCompany);
        }

        private void ParseStockMarketPositionInfo(XmlNode node)
        {
            int nValue;
            string sValue;
            string sColor;
            string sLocation;
            string sSoldOutLocation;
            string sSoldLocation;
            string sPayOutLocation;
            string sHoldLocation;
            StockMarketPosition.eColor color = StockMarketPosition.eColor.White;	// most are this

            sLocation = node.Attributes["Location"].Value;
            sValue = node.Attributes["Value"].Value;
            sColor = node.Attributes["Color"].Value;

            if (sValue != "Closed")
            {
                bool bReturn;
                bReturn = int.TryParse(sValue, out nValue);
            }
            else
                nValue = 0;

            switch (sColor)
            {
                case "Yellow":
                    color = StockMarketPosition.eColor.Yellow;
                    break;

                case "Brown":
                    color = StockMarketPosition.eColor.Brown;
                    break;
            }

            StockMarketPosition position = new StockMarketPosition(sLocation, nValue, color);

            sSoldOutLocation = node["SoldOut"].InnerText;
            sSoldLocation = node["Sold"].InnerText;
            sPayOutLocation = node["PayOut"].InnerText;
            sHoldLocation = node["Hold"].InnerText;

            position.SoldOutLocation = sSoldOutLocation;
            position.SoldLocation = sSoldLocation;
            position.PayOutLocation = sPayOutLocation;
            position.HoldLocation = sHoldLocation;

            _aStockMarketPositions.Add(position);
        }


        public void AdjustGamePhaseInfo(string sTrain, string sRusts)	// adjust various game info based on a train purchase
        {
            string sPhase = "Phase" + sTrain;	// Phase2 or PhaseD

            GamePhaseInfo phaseInfo = (GamePhaseInfo)GamePhasesInfo[sTrain];

            if (phaseInfo != null)
            {
                int nCurrentTrainLimit = TrainLimit;

                TrainLimit = phaseInfo.TrainLimit;
                NumberOfOperatingRoundsNext = phaseInfo.OperatingRounds;

                switch (phaseInfo.TilesAvailable)
                {
                    case "Yellow":
                        YellowTilesAvailable = true;
                        break;

                    case "Green":
                        GreenTilesAvailable = true;
                        break;

                    case "Brown":
                        BrownTilesAvailable = true;
                        break;

                    case "Gray":
                        GrayTilesAvailable = true;
                        break;
                }

                // do specific handling for new trains
                if (sRusts != "")								// do any trains get rusted?
                    RustTrains(sRusts);

                if (nCurrentTrainLimit > phaseInfo.TrainLimit)	// need to reduce the # of trains in each company
                    ReduceCompanyTrainLimits();

                if (sTrain == "3")								// can buy privates now
                {
                    CanBuyPrivateCompanies = true;
                }

                if (sTrain == "5")								// close privates
                {
                    AdjustOffboardHexLocationValues(1);		// Use the 2nd value.  Values are number 0, 1, 2, and 3.
                    CanBuyPrivateCompanies = false;
                    ClosePrivateCompanies();
                }

                if (sTrain == "6")								// CGR forms
                {
                    // jeff handles port removal
                    FormCGR_Start();
                    UpdateAllPlayers ();
                }

                if (sTrain == "8" || sTrain == "D")
                {
                    AdjustOffboardHexLocationValues(3);		// Use the last value of four values.
                }
            }
        }
        private void AdjustOffboardHexLocationValues(int nIndex)
        {
			foreach (GameboardHex hex in this.Gameboard.GameboardHexes)
            {
                if (hex.HexType == GameboardHex.eHexType.OffBoard)
                {
                    string[] sArray = new string[4];

                    sArray = hex.Values.Split(',');
                    int nValue = 0;
                    bool bReturn = int.TryParse(sArray[nIndex], out nValue);
                    hex.Value = nValue;
                }
            }
        }

        public void AdjustOperatingRoundInfo()
        {
            CurrentOperatingRound = 0;
            NumberOfOperatingRounds = NumberOfOperatingRoundsNext;
        }
        private void ReduceCompanyTrainLimits()
        {
            foreach (PublicCompany company in PublicCompanies)
                company.ReduceCompanyTrainLimits();
        }
        private void RustTrains(string sTrainToRust)
        {
            TrainCollection trains = GetTrainCollectionFromType(sTrainToRust);

            int nTrains = trains.Count - 1;

            //  rust the game trains
            while (nTrains >= 0)
            {
                Train train = trains[nTrains];	// get a train

                if (train.Name == sTrainToRust)
                    trains.Remove(train);

                nTrains--;
            }

            //  rust the open market trains
//            foreach (Train train in Trains_OpenMarket)  // JWF 10.23.07  Exception modifying collection
            for (int i = Trains_OpenMarket.Count - 1; i >= 0; i--)  // JWF 10.23.07  Exception modifying collection
            {
                if (Trains_OpenMarket[i].Name == sTrainToRust)
                    Trains_OpenMarket.Remove(Trains_OpenMarket[i]);
            }

            //  rust the companies trains
            foreach (PublicCompany company in PublicCompanies)
                company.RustTrains(sTrainToRust);
        }
        private void ClosePrivateCompanies()
        {
            //  close the players privates
            foreach (Player player in Players)
                player.ClosePrivateCompanies();

            //  close the companies privates
            foreach (PublicCompany company in PublicCompanies)
                company.ClosePrivateCompanies();
        }
        
        public void FormCGR_Start()
        {
            // if CGR formed, get out
            if (CGRFormed)
                return;

            this.DoneDistributingCGRShares   = false;
            this.DistributingCGRShares       = false;
            PublicCompany cgr = PublicCompanies["CGR"];

            // repay as many loans for each company as possible (if has any loans)
			foreach (PublicCompany company in PublicCompanies)
				while (company.RepayLoan()) ;

            CGRForming                      = CanCGRForm();	// ask only if at least one possible choice!
			if (CGRForming)
			{
				CGRFormationPlayerName = CurrentOperatingOwner();
				CGRFormationPriorityPlayerName = CGRFormationPlayerName;
				UpdateAllPlayers();

				// need to prompt each player if they want to keep there companies with loans
				// if keep, take out of players cash, else folds into CGR
				HttpContext.Current.Server.Transfer("CGRFormation.aspx");
			}
			else
				CGRFormed = true;	// we're all done with CGR
        }

		private bool CanCGRForm()
		{
			foreach (PublicCompany company in PublicCompanies)
			{
				if (company.Loans > 0)
					return true;
			}
			return false;
		}

        public void FormCGR_FinishCleanUp ()    // JWF 10.29.2007 The third and final phase of CGR formation.
        {
            PublicCompany cgr = PublicCompanies["CGR"];

            // transfer all cash, trains and special abilities into CGR
            RemovedClosedCompaniesStations();

            int     nFoldingCompanies       = 0;
            int     nAveragingCompanies     = 0;
            int     nStockValue             = 450;
            int     nSkipStockCompany       = -1;
            bool    bAddIn                  = false;
            bool    bFoldingCompanyOperated = false;   
            bool    bCompanyFoldedIntoCGR   = false;   // It's possible, but unlikely, that NO public companies are folded.

            // find the lowest stock value folding company
            for (int i = PublicCompanies.Count - 1; i >= 0; i--)  
            {
                PublicCompany company = PublicCompanies[i];

                if (company.FoldIntoCGR)
                {
                    if (company.StockMarketPosition.Value <= nStockValue)
                    {
                        nStockValue = company.StockMarketPosition.Value;
                        nSkipStockCompany = i;
                    }

                    nFoldingCompanies++;
                }
            }

            nStockValue = 0;
            
            // do some cleanup work and figure CGR stock price
            for (int i = PublicCompanies.Count - 1; i >= 0; i--)  // JWF 10.23.07  Exception modifying collection if using foreach iteration
            {
                PublicCompany company = PublicCompanies[i];

                if (company.FoldIntoCGR)
                {
                    bCompanyFoldedIntoCGR = true;
                    company.CollapseCompanyIntoCGR(cgr);
                    bFoldingCompanyOperated |= company.OperatedThisOperatingRound;

                    if (nFoldingCompanies < 3)  // if 1 or 2 companies, add up and divide by #
                    {
                        bAddIn = true;
                    }
                    else                        // if 3 or more, drop lowest and average rest
                    {
                        if (i != nSkipStockCompany)
                            bAddIn = true;
                        else
                            bAddIn = false;
                    }

                    if (bAddIn)
                    {
                        nStockValue += company.StockMarketPosition.Value;
                        nAveragingCompanies++;
                    }
                }
            }

            if (bCompanyFoldedIntoCGR)
            {
                //set CGR par price (drop lowest and average rest) nearest value...can be 100, 110, 125, 150, 175, 200, ...
                float               fAvgValue       = (float) ((float) nStockValue / (float) nAveragingCompanies);
                //StockMarketPosition marketPosition  = cgr.StockMarketPosition;
                
                // if avg less than or equal 100, set to 100
                if (fAvgValue < 105)
                {
                    cgr.ParPrice = 100;
                    cgr.StockMarketPosition = StockMarketPositions["A5"];
                }
                else if (fAvgValue < 117.5)
                {
                    cgr.ParPrice = 110;
                    cgr.StockMarketPosition = StockMarketPositions["A6"];
                }
                else
                {
                    int     nPos = (int) (((fAvgValue + (float) 50) / (float) 25) + 0.5);
                    string  sPos = "A" + nPos.ToString();

                    cgr.ParPrice = (nPos - 2) * 25;
                    cgr.StockMarketPosition = StockMarketPositions[sPos];
                }

                // update new certificate limit
                CalculateNewCertificateLimit();

                //go thru all trains as can only keep 3 so keep 6's, 5's and then 4's...dump the rest into the open market
                while (cgr.Trains.Count > 3)
                {
                    RemoveTrainFromCgr(cgr, false);
                }

                // if CGR owner said discard 4 trains, then dump any we hold into open market
                if (Discard4s)
                {
                    while (RemoveTrainFromCgr(cgr, true) != null);
                }

                // after train transfers and cleanup, mark if CGR has/had a permanant so no borrow possible
                cgr.HadPermanantTrain           = cgr.HasPermanantTrain();
                cgr.NeedDestination             = false;
                cgr.ReachedDestination          = true;
                cgr.Escrow                      = 0;
                cgr.FullyFunded                 = true;
                cgr.RunningOrder                = cgr.StockMarketPosition.RunningOrder++;
                cgr.OperatedThisOperatingRound  = bFoldingCompanyOperated;
            }
            else
            {
                PublicCompanies.Remove(cgr);
            }

            CGRFormed   = true;
            CGRForming  = false;
            
            this.SaveGame("");  // JWF 10.28.2007  Save game here so that CGR formation can not be undone!        
        }

        private Train RemoveTrainFromCgr(PublicCompany cgr, bool bDumpOnly4s)
        {
            Train removalTrain = null;
            
            foreach (Train train in cgr.Trains)
            {
                if (train.Name.Equals ("4"))
                {
                    removalTrain = train;
                    break;
                }
            }

            if (!bDumpOnly4s)
            {
                if (removalTrain == null)
                {
                    foreach (Train train in cgr.Trains)
                    {
                        if (train.Name.Equals("5"))
                        {
                            removalTrain = train;
                            break;
                        }
                    }
                }

                if (removalTrain == null)
                {
                    foreach (Train train in cgr.Trains)
                    {
                        if (train.Name.Equals("6"))
                        {
                            removalTrain = train;
                            break;
                        }
                    }
                }
            }

            // don't try to remove or add a null train
            if (removalTrain != null)
            {
                cgr.Trains.Remove (removalTrain);
                Trains_OpenMarket.Add (removalTrain);
            }

            return removalTrain;
        }
        
        public bool DistributingCGRShares { get; set; }
        public bool DoneDistributingCGRShares { get; set; }      
        
        public void FormCGR_Finish()
        {
            // The distribution of shares should only be done by one player and not all of the players
            if (!this.DistributingCGRShares)
            {
                this.DistributingCGRShares      = true;
                DistributeCGRShares();      // JWF 10.27.2007  Must be done before collapsing company.
                DetermineCGRPresident();    // JWF 10.27.2007  This needs to be done before replacing optional tokens.
                this.DoneDistributingCGRShares  = true;
            }        
            
            while (!this.DoneDistributingCGRShares)
            {
                ;  // Busy loop waiting for CGR share distribution so we know who the CGR owner is for the next step of optional token replacement.
            }
            
            CgrTokenReplacement();
        }

        private void CgrTokenReplacement()
        {
            CgrReplaceHomeStations ();
            this.CgrOptionalTokenReplacement = true;
            CgrReplaceOptionalStations ();  // Query user to see if he wants to do token replacement.
        }

        private void RemovedClosedCompaniesStations()   // Remove any tokens from the map for closed companies.
        {
            foreach (PublicCompany publicCompany in PublicCompanies)
            {
                if (publicCompany.FoldIntoCGR)
                {
                    foreach (GameboardHex hex in publicCompany.TokenHexes)
                    {
                        for (int nIndex = 0; nIndex < hex.Tokens.Count; nIndex++)
                        {
                            Token removalToken = hex.Tokens[nIndex];
                            
                            if (removalToken.Company.Name.Equals(publicCompany.Name))
                            {
                                hex.Tokens.Remove(removalToken);
                                hex.m_nNumTokens = hex.m_nNumTokens - 1;  // JWF 11.13.2007  Fix defect where token couldn't be placed when it should be able to.
                            }
                        }
                    }
                }
            }        
        }

        private void CgrReplaceOptionalStations()   // JWF 10.27.27  TODO  Query user for optional CGR token replacement.
        {
//            HttpContext.Current.Server.Transfer ("OperatingRound2.aspx");

            StringCollection scPossibleCgrReplacementTokens = new StringCollection();

            foreach (PublicCompany publicCompany in PublicCompanies)
            {
                if (publicCompany.FoldIntoCGR)
                {
                    foreach (GameboardHex hex in publicCompany.TokenHexes)
                    {
                        for (int nIndex = 0; nIndex < hex.Tokens.Count; nIndex++)
                        {
                            Token replacementToken = hex.Tokens[nIndex];

                            if (replacementToken.Company.Name.Equals(publicCompany.Name))
                            {
                                if (!CgrHasTokenInHex (hex))  // CGR might already have a token in this hex from home station replacement.
                                {
                                    scPossibleCgrReplacementTokens.Add(hex.Location + " - " + replacementToken.Company.Name);
                                }
                            }
                        }
                    }
                }
            }   
            
			HttpContext.Current.Session ["PossibleReplacementTokens"] = scPossibleCgrReplacementTokens;

			// Joe 9/26/2010 if no possible tokens to replace, we're done
			if (scPossibleCgrReplacementTokens.Count == 0)
			{
				this.CGRFormed = true;
				this.CGRForming = false;
				this.CgrOptionalTokenReplacement = false;
				HttpContext.Current.Server.Transfer("OperatingRound2.aspx");
			}
        }

        public bool CgrHasTokenInHex(GameboardHex hex)
        {
            bool    bReturn = false;
            PublicCompany cgrCompany = PublicCompanies["CGR"];  // need the cgr company for share creation
            
            foreach (Token existingToken in hex.Tokens)
            {
                if (existingToken.Company.Name.ToLower ().Equals (cgrCompany.Name.ToLower ()))
                {
                    return true;
                }
            }
            
            return bReturn;
        }

        private void CgrReplaceHomeStations()   // JWF 10.27.2007  Replace home station tokens with CGR tokens for all companies folding into the CGR.
        {
            PublicCompany cgrCompany = PublicCompanies["CGR"];  // need the cgr company for share creation
            cgrCompany.Operated = true;   //  Need to set so that it won't be queried to place an initial token.
            
            foreach (PublicCompany publicCompany in PublicCompanies)
            {
                if (publicCompany.FoldIntoCGR)
                {
                    foreach (GameboardHex hex in publicCompany.TokenHexes)
                    {
                        if (hex.Location == publicCompany.Home)
                        {
                            for (int nIndex = 0; nIndex < hex.Tokens.Count; nIndex++)
                            {
                                Token removalToken = hex.Tokens[nIndex];
                                
                                if (removalToken.Company.Name.Equals (publicCompany.Name))
                                {
                                    hex.Tokens.Remove(removalToken);

                                    Token CgrToken = new Token();

                                    CgrToken.LocationInHex = removalToken.LocationInHex;
                                    CgrToken.Company = cgrCompany;
                                    hex.Tokens.Add(CgrToken);
                                    cgrCompany.TokenHexes.Add (hex);
                                    cgrCompany.TokensPlayed++;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        private void DistributeCGRShares()
        {
            PublicCompany   cgrCompany                          = PublicCompanies["CGR"];  // need the cgr company for share creation
            int             nTotalNumberOfCgrShares             = 0;  // JWF 10.27.2007  Max 20, if more than 10 then only 5% shares.
            int             nPlayerShares                       = 0;  // JWF 10.27.2007  Max 20, if more than 10 then only 5% shares.
            int             nNumberOfOpenMarketSharesToTradeIn  = 0;
            bool            bHalfWayToCgrShare                  = false;
            int             nPos                                = 0;
            int             nCGRFormingPlayerPos                = 0;
            Player          player                              = null;

            // to find president, start with forming player and wrap around until back to forming player
            player = Players[nCGRFormingPlayerPos];

            // step 0.  find the forming player
            while (player.Name != CGRFormationPlayerName)
                player = Players[++nCGRFormingPlayerPos];

            nPos = nCGRFormingPlayerPos;

            // step 1.  forming player to end
            while (nPos < Players.Count)
            {
                player = Players[nPos++];
                
                int nThisPlayerCgrShares = 0;

                foreach (PublicCompany company in PublicCompanies)
                {
                    if (company.FoldIntoCGR && (nPlayerShares < 20))
                    {
                        foreach (Share share in player.Shares)
                        {
                            if (share.PublicCompany.Name.Equals(company.Name))
                            {
                                if (share.President)  // Presidential certificates count as two shares!
                                {
                                    nPlayerShares++;
                                    nThisPlayerCgrShares++;

                                    if (nPlayerShares >= 20)  // Distribute shares in priority oreder.  Can only distribute 20 shares.  Other players are screwed if not enough.  Thus, the order is important. 
                                    {
                                        break;
                                    }
                                }
                                else
                                {
                                    if (bHalfWayToCgrShare)
                                    {
                                        nPlayerShares++;
                                        nThisPlayerCgrShares++;
                                        nNumberOfOpenMarketSharesToTradeIn--;
                                        bHalfWayToCgrShare = false;

                                        if (nPlayerShares >= 20)  // Distribute shares in priority oreder.  Can only distribute 20 shares.  Other players are screwed if not enough.  Thus, the order is important. 
                                        {
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        bHalfWayToCgrShare = true;
                                        nNumberOfOpenMarketSharesToTradeIn++;
                                    }
                                }
                            }
                        }
                    }
                }

                for (int i = 0; i < nThisPlayerCgrShares; i++)
                {
                    Share cgrShare = new Share(10, cgrCompany, false);  // JWF Later fixup if 5% shares or presidency.

                    player.Shares.Add(cgrShare);
                }
            }

            nPos = 0;

            // step 2.  beginning to forming player
            while (nPos < nCGRFormingPlayerPos)
            {
                player = Players[nPos++];
                
                int nThisPlayerCgrShares = 0;

                foreach (PublicCompany company in PublicCompanies)
                {
                    if (company.FoldIntoCGR && (nPlayerShares < 20))
                    {
                        foreach (Share share in player.Shares)
                        {
                            if (share.PublicCompany.Name.Equals(company.Name))
                            {
                                if (share.President)  // Presidential certificates count as two shares!
                                {
                                    nPlayerShares++;
                                    nThisPlayerCgrShares++;

                                    if (nPlayerShares >= 20)  // Distribute shares in priority oreder.  Can only distribute 20 shares.  Other players are screwed if not enough.  Thus, the order is important. 
                                    {
                                        break;
                                    }
                                }
                                else
                                {
                                    if (bHalfWayToCgrShare)
                                    {
                                        nPlayerShares++;
                                        nThisPlayerCgrShares++;
                                        nNumberOfOpenMarketSharesToTradeIn--;
                                        bHalfWayToCgrShare = false;

                                        if (nPlayerShares >= 20)  // Distribute shares in priority oreder.  Can only distribute 20 shares.  Other players are screwed if not enough.  Thus, the order is important. 
                                        {
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        bHalfWayToCgrShare = true;
                                        nNumberOfOpenMarketSharesToTradeIn++;
                                    }
                                }
                            }
                        }
                    }
                }

                for (int i = 0; i < nThisPlayerCgrShares; i++)
                {
                    Share cgrShare = new Share(10, cgrCompany, false);  // JWF Later fixup if 5% shares or presidency.

                    player.Shares.Add(cgrShare);
                }
            }
                       
            // Now exchange the shares in the open market.
            foreach (PublicCompany company in PublicCompanies)
            {
                if (company.FoldIntoCGR && (nPlayerShares < 20))
                    nNumberOfOpenMarketSharesToTradeIn += company.OpenMarketShares.Count;
            }

            int nOpenMarketCGRShares = nNumberOfOpenMarketSharesToTradeIn / 2;   // JWF 10.27.2007  Trade in 2 for 1 dropping fractions.

            if (nOpenMarketCGRShares + nPlayerShares > 20)
                nOpenMarketCGRShares = 20 - nPlayerShares;
            
            for (int i = 0; i < nOpenMarketCGRShares; i++)
            {
                Share openMarketShare = new Share (10, cgrCompany, false);
                cgrCompany.OpenMarketShares.Add (openMarketShare);
            }

            nTotalNumberOfCgrShares = nPlayerShares + nOpenMarketCGRShares;
            
            int nPercentage = 10;
            int nIpoShares  = 10 - nTotalNumberOfCgrShares;
            
            if (nTotalNumberOfCgrShares > 10)    // JWF 10.27.2007  5% CGR shares?
            {
                cgrCompany.FullShares = false;
                nPercentage = 5;
                nIpoShares = 20 - nTotalNumberOfCgrShares;
                
                foreach (Share openMarketShare in cgrCompany.OpenMarketShares)
                    openMarketShare.Percentage = 5;
                
                foreach (Player players in Players)
                {
                    foreach (Share share in players.Shares)
                    {
                        if (share.PublicCompany.Name.Equals (cgrCompany.Name))
                            share.Percentage = 5;
                    }
                }
            }
            
            //  Now add in the IPO shares.
            for (int i = 0; i < nIpoShares; i++)
            {
                Share ipoShare = new Share (nPercentage, cgrCompany, false);
                cgrCompany.IPOShares.Add (ipoShare);
            }
        }

        private void DetermineCGRPresident()
        {
            int             nMaxCgrShares           = 0;
            int             nCurrentPlayerCgrShares = 0;
            int             nPos                    = 0;
            int             nCGRFormingPlayerPos    = 0;
            Player          player                  = null;
            Player          president               = null;
            PublicCompany   cgrCompany              = PublicCompanies["CGR"];  // need the cgr company for share modification

            // to find president, start with forming player and wrap around until back to forming player
            player = Players[nCGRFormingPlayerPos];
            
            // step 0.  find the forming player
            while (player.Name != CGRFormationPlayerName)
                player = Players[++nCGRFormingPlayerPos];
            
            nPos = nCGRFormingPlayerPos;

            // step 1.  forming player to end
            while (nPos < Players.Count)
            {
                player                  = Players[nPos++];
                nCurrentPlayerCgrShares = 0;

                foreach (Share share in player.Shares)
                {
                    if (share.PublicCompany.Name.Equals(cgrCompany.Name))
                        nCurrentPlayerCgrShares++;
                }
                
                if (nCurrentPlayerCgrShares > nMaxCgrShares)
                {
                    nMaxCgrShares   = nCurrentPlayerCgrShares;
                    president       = player;
                }
            }
            
            nPos = 0;

            // step 2.  beginning to forming player
            while (nPos < nCGRFormingPlayerPos)
            {
                player                  = Players[nPos++];
                nCurrentPlayerCgrShares = 0;

                foreach (Share share in player.Shares)
                {
                    if (share.PublicCompany.Name.Equals(cgrCompany.Name))
                        nCurrentPlayerCgrShares++;
                }

                if (nCurrentPlayerCgrShares > nMaxCgrShares)
                {
                    nMaxCgrShares   = nCurrentPlayerCgrShares;
                    president       = player;
                }
            }
            
            if (president != null)  // We have elected a president!
            {
                if (nMaxCgrShares > 1)  // Do a removal of one CGR share and a modification of a second CGR share if the president has two shares.  If the president only has one share than just modify it.
                {
                    //  first remove a CGR share.
                    for (int i = 0; i < president.Shares.Count; i++)
                    {
                        Share currentShare = president.Shares[i];

                        if (currentShare.PublicCompany.Name.Equals(cgrCompany.Name))
                        {
                            president.Shares.Remove(currentShare);
                            break;
                        }
                    }
                }

                foreach (Share share in president.Shares)
                {
                    if (share.PublicCompany.Name.Equals(cgrCompany.Name))
                    {
                        share.Percentage *= 2;
                        share.President = true;
                        break;
                    }
                }

                // set the owner as the president..this is very important or crashes will result
                cgrCompany.Owner = president;
            }
        }

        public void CalculateNewCertificateLimit()
        {
            // this needs to be done after 1st 6 train bought and anytime after a company closes
            int nPlayers        = Players.Count;
            int nCompaniesLeft  = 0;

            foreach (PublicCompany company in PublicCompanies)
            {
                if (company.CompanyAvailable)
                    nCompaniesLeft++;
            }

            ////											  3	  4   5   6		players left
            //int[,] aCertificateLimits = new int[,] {	 {20, 16, 13, 11},	// 12 companies left
            //                                             {28, 22, 18, 15},	// 11 companies left
            //                                             {25, 20, 16, 14},	// 10 companies left
            //                                             {22, 18, 15, 12},	// 09 companies left
            //                                             {20, 16, 13, 11},	// 08 companies left
            //                                             {18, 14, 11, 10},	// 07 companies left
            //                                             {15, 12, 10,  8},	// 06 companies left
            //                                             {13, 10,  8,  7},	// 05 companies left
            //                                             {10,  8,  7,  6},	// 04 companies left
            //                                             {10,  8,  7,  6},	// 03 companies left    JWF 10.23.07  Should never be less than 4 public companies but handle this case anyway.
            //                                             {10,  8,  7,  6},	// 02 companies left
            //                                             {10,  8,  7,  6}};	// 01 companies left
            //// adjust index values to get correct bucket
            //CertificateLimit = aCertificateLimits[11 - nCompaniesLeft, nPlayers - 3];

            //											  1	  2   3   4   5   6		players left
            int[,] aCertificateLimits = new int[,] {	{30, 25, 20, 16, 13, 11},	// 12 companies left
														{40, 34, 28, 22, 18, 15},	// 11 companies left
														{37, 31, 25, 20, 16, 14},	// 10 companies left
														{34, 29, 22, 18, 15, 12},	// 09 companies left
														{31, 25, 20, 16, 13, 11},	// 08 companies left
														{29, 23, 18, 14, 11, 10},	// 07 companies left
														{24, 19, 15, 12, 10,  8},	// 06 companies left
														{20, 16, 13, 10,  8,  7},	// 05 companies left
														{17, 12, 10,  8,  7,  6},	// 04 companies left
														{17, 12, 10,  8,  7,  6},	// 03 companies left  JWF 10.23.07  Should never be less than 4 public companies but handle this case anyway.
														{17, 12, 10,  8,  7,  6},	// 02 companies left
														{17, 12, 10,  8,  7,  6}};	// 01 companies left

            // adjust index values to get correct bucket
            CertificateLimit = aCertificateLimits[12 - nCompaniesLeft, nPlayers - 1];
        }


        public void CheckForDestinations()	// Check all public companies to see if they've reached their destinations.
        {
            foreach (PublicCompany company in this.PublicCompanies)
                company.CheckForDestination();
        }

        public void CheckForClosedCompanies()
        {
            bool bFound = false;

            while (!bFound)
            {
                foreach (PublicCompany company in PublicCompanies)
                {
                    if (company.StockMarketPosition != null)	// has it been opened?
                    {
                        if (company.Closed  && company.CompanyAvailable)
                        {
                            company.CloseCompany(true);			// close the sucker down

                            if (CGRFormed)
                                CalculateNewCertificateLimit();

                            bFound = true;
                            break;
                        }
                    }
                }

                if (bFound)
                    bFound = false;	// reset and to see if another company
                else
                    break;
            }
        }
        private bool PrivateOwnedByPlayer(string sPrivate)
        {
            bool bReturn = false;

            foreach (Player player in this.Players)
            {
                foreach (PrivateCompany privateCompany in player.PrivateCompanies)
                {
                    if (privateCompany.Name == sPrivate)
                    {
                        bReturn = true;
                        return bReturn;
                    }
                }
            }

            return bReturn;
        }

        public bool TunnelAvailable()
        {
            bool bReturn = false;

            if (_tunnelsForSale > 0)
            {
                if (!PrivateOwnedByPlayer("St. Clair Frontier Tunnel Company"))
                {
                    bReturn = true;
                }
            }

            return bReturn;
        }

        public bool BridgeAvailable()
        {
            bool bReturn = false;

            if (_bridgesForSale > 0)
            {
                if (!PrivateOwnedByPlayer("Niagra Falls Suspension Company"))
                {
                    bReturn = true;
                }
            }

            return bReturn;
        }

        public void PurchaseBridgeRights()
        {
            this.CurrentlyOperatingCompany.Cash -= 50;
            PrivateCompany privateCompany = null;

            foreach (PublicCompany publicCompany in this.PublicCompanies)
            {
                if (publicCompany.Owns("Niagra Falls Suspension Company", ref privateCompany))
                {
                    publicCompany.Cash += 50;
                    break;
                }
                else
                {
                    this.Bank += 50;
                }
            }

            this.CurrentlyOperatingCompany.BridgeRights = true;
            _bridgesForSale--;
        }

        public void PurchaseTunnelRights()
        {
            this.CurrentlyOperatingCompany.Cash -= 50;
            PrivateCompany privateCompany = null;

            foreach (PublicCompany publicCompany in this.PublicCompanies)
            {
                if (publicCompany.Owns("St. Clair Frontier Tunnel Company", ref privateCompany))
                {
                    publicCompany.Cash += 50;
                    break;
                }
                else
                {
                    this.Bank += 50;
                }
            }

            this.CurrentlyOperatingCompany.TunnelRights = true;
            _tunnelsForSale--;
        }

        public bool InStockRound()
        {
/*        
            bool bReturn = false;
            
            if ((History != null) && (History.Count > 0))
            {
                string sFirst = History[0];
                
                if (sFirst.ToLower ().StartsWith ("stock"))
                {
                    bReturn = true;
                }
            }
  */          
            return this.GoToStockRound;  // JWF 12.3.2007  A better way of determining state.
        }
        
        public void UpdateAllPlayers ()
        {
            string sCurrentPlayer = HttpContext.Current.Session["PlayerName"] as string;
        
            foreach (Player player in Players)
            {
                if (!player.Name.Equals (sCurrentPlayer))  // JWF  11.12.2007  Don't update the current player, it's not necessary as they already got the postback event
                {
                    player.UpdateScreen = true;
                }
            }
        }
    }
}








